Blog Top          Mijinko Semi Top

Mijinko seminar. Produced By SP

Now the Mijinko it is possible to stand up!!('A`)

	

多重起動2−2(CreateMutex)

	集まったか?もうはじめるぞ。
	
	それではお約束の MSDN から CreateMutex を調べてみるか。
	
	
	-- MSDN より抜粋 --
	
	CreateMutex
	名前付きまたは名前なしのミューテックス(mutually exclusive;相互排他)オブジェクトを作成または開きます。
	
	HANDLE CreateMutex(
	LPSECURITY_ATTRIBUTES lpMutexAttributes,  // セキュリティ記述子
	BOOL bInitialOwner,                       // 最初の所有者
	LPCTSTR lpName                            // オブジェクトの名前
	);
	
	ミューテックスオブジェクトの説明はもういいとして、
	CreateMutex を使うには3つの引数に適切な値を入れないといけないらしい。
	
	lpMutexAttributes
	bInitialOwner
	lpName
	
	しかしまぁ、相変わらず不親切な説明文だな。
	ざっくり説明するから読んでおけ。
	
	最初の lpMutexAttributes はミューテックスオブジェクト自体の振る舞いを定義する SECURITY_ATTRIBUTES構造体を指定する箇所だ。
	説明文にはセキュリティ記述子などと偉そうに書かれているが、しょせんはただの設定であり大したものじゃない。
	特に指定がなければ NULL or 0 を指定しておけばいい。ぶっちゃけ俺もこの値をいちいち指定したことはない。
	
	次に bInitialOwner、ここには作成するミューテックスオブジェクトを保持するかどうかの指定をする。
	少しまぎらわしいのだがミューテックスオブジェクトを作成するのとミューテックスオブジェクトを保持するのでは意味が異なる。
	噛み砕いていえば、ミューテックスオブジェクトを作っただけで置いておくか、作ったから持っていくか、を決める場所だ。
	値の指定には TRUE(1) か FALSE(0) を指定する。
	
	最後の lpName には作成するミューテックスオブジェクトの名前を指定する。
	たとえば過疎ゲーの MICMAC の場合はここに micmac という文字列が指定されている。
	つまり micmac という名のミューテックスオブジェクトが生成されるわけだ。
	このミューテックスオブジェクトの名前の指定はなくてもいい。
	その場合には名無しのミューテックスオブジェクトが生成されることになる。
	
	ちなみに CreateMutex API をC言語で記述するとこのようになる。
	
	CreateMutex(0, 1, "micmac")
	
	
	さて、ある程度の説明が終わったところで CreateMutex のサンプルプログラムを用意したからダウンロードしておけ
	
		
	
	ダウンロードは出来たか?それならさっそく実行させてみろ。
	
	
	
	「おいでやす」
	
	
	
	うるさいよお前、とつっこみたくなるが気にせずにこの状態でもう一つ立ち上げてみろ。
	「既に起動しています」というポップアップがでたはずだ。つまり 多重起動 の対策がされているわけだ。
	
	これまで CreateMutex の仕様・動作を学んできたがここからが本番だ。
	ここまで付いてこれたおまえなら大丈夫だとは思うが、あと一息だから置いてかれないようについてこいよ。
	
	CreateMutex における多重起動のチェック方法は、意図したミューテックスオブジェクトが生成できたかどうか、
	の確認を行うことにより実現されている。
	ではどうやったらミューテックスオブジェクトが生成できた or 生成出来なかったと判断できるのだろうか?
	
	もしおまえが CreateMutex API の戻り値を見て判断している、と思ったのならそれは間違いだ。
	詳しくは後述するが、今回のケースでは CreateMutex の戻り値を期待しているわけではないからだ。
	しかしその考えは正しい考え方だ。必ず役に立つときが来るから忘れるな。
	
	念のため一言残しておこう。
	
	APIの処理が終わったときには必ずAPIの戻り値(EAXに格納される)はなんだったのかを確認しておけ
	
	
	
	さて、話を戻すが API や関数が終了したときの成否、今回で言えば CreateMutex を実行した結果として
	ミューテックスオブジェクトが生成できたかどうか?は GetLastError API を用いることで確認が出来る。
	まずは MSDN から GetLastError を見てみるか。
	
	
	
	-- MSDN より抜粋 --
	
	GetLastError
	呼び出し側のスレッドが持つ最新のエラーコードを取得します。エラーコードは、スレッドごとに保持されるため、
	複数のスレッドが互いの最新のエラーコードを上書きすることはありません。

	DWORD GetLastError(VOID);
	パラメータ
	パラメータはありません。

	戻り値
	呼び出し側のスレッドが持つ最新のエラーコードが返ります。他の関数は、SetLastError 関数を呼び出して、
	内部でこのエラーコードを設定します。
	各関数の「戻り値」では、各関数がどのような条件で最新のエラーコードを設定するのか説明しています。
	
	
	今までの中では比較的わかりやすい説明ではあるがやっぱり意味不(ry
	簡単にいうと何らかの処理を実行し、その処理が失敗した場合になぜ失敗したのか? をエラーコードとして返す API だ。
		
	それでは立ち上げたプログラムを全て終了させ、解析を始めるとするか。
	まず Ollydbg でサンプルプログラムを読み込み F8 を3回おしてみろ。CreateMutex を呼び出すところで止まるはずだ。
	
	0040100F  |. E8 EC1F0000    CALL         ; \CreateMutexA
	
	ここでもう一度 F8 を押すと CreateMutex の処理がされミューテックスオブジェクトが生成されることになるが、
	先ほども言ったように EAX に格納される CreateMutex の戻り値 は気にするな。
	
	そして更に下をみると GetLastError の処理が見えるだろう。ここからがキーとなる。
	
	00401015  |. E8 EC1F0000    CALL GetLastError>        ; [GetLastError
		
	ここでの GetLastError は、事前に実行した処理(CreateMutex)の結果をエラーコードとして
	返してくるはずだ。それではう一度 F8 を押し、GetLastError の戻り値を確認してみるんだ。
	
	0040101A  |. 3D B7000000    CMP EAX,0B7
	
	EAX には何が格納されたか確認できたな?
	
	さて、今操っている Ollydbg はそのまま放置プレイとして、多重起動のチェックというからには
	もう一つプログラムを立ち上げてみようか。無論 Ollydbg で読み込んで立ち上げるんだぞ。
	2つ目の Ollydbg も放置プレイしている Ollydbg と同じく、アドレス 0040101A の処理まで進めてみろ。
	そしてこのとき GetLastError の戻り値が格納される EAX には何が入っているか確認するんだ。
	
	
	そう、今度は EAX に B7 という値が格納されている。。
	これによって、たった今実行しようとしている cmp 命令は EAX(B7) と B7 を比較しようとしているのがわかるだろう。
	更にその下には JE 命令が潜んでいる。
	
	0040101F  |. 74 27          JE SHORT createmu.00401048
	
	JE 命令は ZFフラグが 1 の時に指定したアドレスへ処理を飛ばす命令だ。
	JE 命令をクリックして Enter してみればわかるが、ジャンプ先は「既に起動してます。」のメッセージボックスである。
	
	それでは少し整理をしてみようか。
	
	1.CreateMutex を実行し、ミューテックスオブジェクトが生成できたかどうかを GetLastError で確認する。
	
	2.1つ目に立ち上げたプログラムでは GetLastError の戻り値は 0 (成功) となり、
	  2つ目のプログラムでは GetLastError の戻り値が B7 (エラー) となる。
	  
	3.cmp 命令で比較しているのが EAX と B7 であるが、もし GetLastError の戻り値が格納されている EAX が
	  B7 であった場合、EAX == B7、つまり比較の結果は等しいこととなり、ZFフラグに1がたつ。
	  
	4.cmp 命令の更に次にある JE 命令は ZFフラグが1の時に指定したアドレスへ処理を飛ばす。
	  飛ばされる先は「既に起動してます。」のメッセージボックスである。
	  
	これが一連の流れである。
	ところでプログラムを見る限り、CreateMutex によるミューテックスオブジェクトの生成が失敗した場合に 
	GetLastErrorB7 というエラーコードを返してくるのはわかったが具体的に B7 とはなんであろうか?

	これを解決するには error.h というファイルを確認すればいい。
	error.h は色々なエラーの定義がされているヘッダーファイルだ。
	
	Borland C++ をインストールしてあれば \borland\bcc55\Include フォルダに
	error.h というファイルがあるのを確認できただろう。

	それでは早速検索してみるんだ。


	ん?無い?


	B7 はあくまで 16進数で表現した値だ、error.h ではエラーコードは 10進数で定義されている。
	よって、B7 を 10進数の 183 に直し、検索すれば以下の一文がみつかったはずだ。
	
	#define ERROR_ALREADY_EXISTS        183
	
	この ERROR_ALREADY_EXISTS は CreateMutex(MSDN)の戻り値の説明文に書いてあったものと同じ値だ。
	
	よし、ではいい加減に CreateMutex による 多重起動 チェックの攻略をするぞ。
	
	いうまでもないが以下の処理が多重のチェックをしている箇所というのはもうわかったよな?
	
	0040101A  |. 3D B7000000    CMP EAX,0B7
	0040101F  |. 74 27          JE SHORT createmu.00401048

	俺たちの残りの仕事はどうやってアプローチしていくか?を考えるだけだ。
	それでは一般的な方法から考えてみるとするか。

	まず思いつくのが、JE 命令のジャンプ先自体を 00401048 から 00401021 へ書き換える方法だ。
	これにより比較の結果がどうあれ、必ず 00401021 へジャンプし、「既に起動してます。」の
	メッセージは表示されなくなる。

	0040101F  |. 74 27          JE SHORT createmu.00401021

	それでは次に考えられる方法はなんだろうか?
	そう、nop 命令による書き換えだ。
	nop 命令は何もしない命令、あまったスペースを塗りつぶすために使われる用途が多いと説明したのは覚えているよな?
	ここでは JE 命令自体を nop で書き換えてしまえば解決する。

	0040101A     3D B7000000    CMP EAX,0B7
	0040101F     90             NOP
	00401020     90             NOP
	

	他に方法はないだろうか?

	もちろんある。
	比較しているのがエラーコードということはつまり、比較したいエラーコード番号(B7)自体を他の値に
	変えてしまえばいいだけだ、例えばこんな感じだ。

	0040101A     3D B8000000    CMP EAX,0B8


	さて、以上の3つの回避方法を教えたところで多重起動2(CreateMutex)の授業は終了となるが
	今回はちと長くなっちまったかもな。やれやれだ。

	やっと次の授業で多重起動編は終了となるが、いまのうちに過去の授業の内容を復習しておけ。
	CreateMutex については実践でやっておけ。
	
	

	それではやることやったミジンコから 多重起動3(EnumWindows、その他の方法)の授業を始めるからついてこい 
	
	
	

Blog Top    Mijinko Semi Top

Copyright (C) 2006 SP-.SEESAA.NET - All Rights Reserved.